package common;

import java.lang.reflect.Field;

import common.data.MessageTypeEnum;
import common.data.RequestTypeEnum;
import common.data.ResponseTypeEnum;

public class DeserializationManager {
	/**
	 * DeserializationManager is a helper that deserialized
	 * byte stream into primitive data, construct the 
	 * serialized object
	 */
	
	private static DeserializationManager current;
	
	public static DeserializationManager getCurrent(){
		if (current == null){
			current = new DeserializationManager();
		}
		return current;
	}

	/*
	 * Primitive data deserialize
	 */
	public int toInt(byte[] byteInt) {
        if (byteInt == null || byteInt.length != Constants.INT_LENGTH) 
            return 0x0;
        
        return (int)(
                (0xFF & byteInt[0]) |
                (0xFF & byteInt[1]) << 8 |
                (0xFF & byteInt[2]) << 16 |
                (0xFF & byteInt[3]) << 24
        );
	}
	
	public long toLong(byte[] byteLong){
        if (byteLong == null || byteLong.length != Constants.LONG_LENGTH) 
            return 0x0;
        
        return (long)(
                (long)(0xFF & byteLong[0]) |
                (long)(0xFF & byteLong[1]) << 8 |
                (long)(0xFF & byteLong[2]) << 16 |
                (long)(0xFF & byteLong[3]) << 24 |
                (long)(0xFF & byteLong[4]) << 32 |
                (long)(0xFF & byteLong[5]) << 40 |
                (long)(0xFF & byteLong[6]) << 48 |
                (long)(0xFF & byteLong[7]) << 56
        );
	}

	public float toFloat(byte[] byteFloat){
		return Float.intBitsToFloat(this.toInt(byteFloat));
	}

	public char toChar(byte byteChar){
        return (char)(
                (0xFF & byteChar));
	}

	public boolean toBool(byte[] data) {
        return (data == null || data.length == 0) ? false : data[0] != 0x00;
    }
	/*
	 * String deserialize
	 */
	public String toString(byte[] byteString) {
		return new String(this.toCharArray(byteString));
	}
	
	public char[] toCharArray(byte[] byteChar){
		char[] result = new char[byteChar.length];
		for (int i=0; i<byteChar.length; i++) {
			result[i] = this.toChar(byteChar[i]);
		}
		return result;
	}

	/*
	 * Enum deserialize
	 */
	public CurrencyEnum toCurrencyEnum(byte[] byteEnum){
		int value = this.toInt(byteEnum);
		return CurrencyEnum.values()[value];
	}
	
	public RequestTypeEnum toRequestTypeEnum(byte[] byteEnum){
		int value = this.toInt(byteEnum);
		return RequestTypeEnum.values()[value];
	}
	
	public MessageTypeEnum toMessageTypeEnum(byte[] byteEnum){
		int value = this.toInt(byteEnum);
		return MessageTypeEnum.values()[value];
	}
	
	public ResponseTypeEnum toResponseTypeEnum(byte[] byteEnum){
		int value = this.toInt(byteEnum);
		return ResponseTypeEnum.values()[value];
	}
	/*
	 * Object reconstruct
	 */
	
	public Object toField(Field field, byte[] fieldStream){
		Object fieldData = null;
		DataTypeEnum fieldType = DataTypeEnum.valueOf(field.getType().getSimpleName());
		switch(fieldType){
		case BOOLEAN:
			fieldData = this.toBool(fieldStream);
			break;
		case BYTE:
			fieldData = fieldStream;
			break;
		case CHAR:
			fieldData = this.toChar(fieldStream[0]);
			break;
		case CURRENCY_ENUM:
			fieldData = this.toCurrencyEnum(fieldStream);
			break;
		case FLOAT:
			fieldData = this.toFloat(fieldStream);
			break;
		case INTEGER:
			fieldData = this.toInt(fieldStream);
			break;
		case LONG:
			fieldData = this.toLong(fieldStream);
			break;
		case MESSAGE_TYPE_ENUM:
			fieldData = this.toMessageTypeEnum(fieldStream);
			break;
		case REQUEST_TYPE_ENUM:
			fieldData = this.toRequestTypeEnum(fieldStream);
			break;
		default:
			break;
		}
		return fieldData;
	}
}
